use common_base::{prelude::*, serde_json};

use crate::data::FormDataModel;

use super::ContextValue;

#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum DataObject {
    String(String),
    ArrayList(Vec<ContextValue>),
    #[serde(skip)]
    FormDataModel(SharedRef<FormDataModel>),
    // ……
}

// this is dangarous because `SharedRef` must used between threads
unsafe impl Send for DataObject {}

impl DataObject {
    pub fn deserizlize_from_json(
        mut data_obj_json: serde_json::Value,
    ) -> Result<Option<DataObject>> {
        if data_obj_json.as_null().is_some() {
            return Ok(None);
        } else if data_obj_json.as_object().is_none() {
            return Err(ErrorCode::RequestInvalid(
                "dataObject is not null or object.",
            ));
        }

        match data_obj_json.get("type").and_then(|v| v.as_str()) {
            Some("string") => {
                if let Some(str_data) = data_obj_json.get("data").and_then(|v| v.as_str()) {
                    Ok(Some(DataObject::String(str_data.to_string())))
                } else {
                    Err(ErrorCode::RequestInvalid(
                        "can not find valid data when DataObject type is string",
                    ))
                }
            }
            Some("ArrayList") => {
                if let Some(array_list) = data_obj_json.get("data").and_then(|v| v.as_array()) {
                    Ok(Some(DataObject::ArrayList(array_list.clone())))
                } else {
                    Err(ErrorCode::RequestInvalid(
                        "can not find valid data when DataObject type is ArrayList",
                    ))
                }
            }
            Some("EFFormDataModel") => {
                if let Some(data) = data_obj_json.get_mut("data").and_then(|v| Some(v.take())) {
                    Ok(Some(FormDataModel::deserizlize_from_json(data)?.into()))
                } else {
                    Err(ErrorCode::RequestInvalid(
                        "can not find valid data when DataObject type is EFFormDataModel",
                    ))
                }
            }
            // EFDataSet
            // EFRowSet
            // EFMDMDataModel
            // ColumnModelData
            // JQueryStubObject
            // JResponseObject
            // ServiceContainerStub
            _ => todo!(),
        }
    }

    pub fn serializes_into_bincode(&self) -> Result<Vec<u8>> {
        common_base::bincode::serialize(self).map_err(|e| ErrorCode::from(e))
    }
}
